
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;

using System.Xml;
using System.Xml.XPath;

using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Core;

namespace BackupRestoreDeploy
{

	public class BackUpBizRules : Syrinx.BaseBizRules
	{
		public void executeCompanyBackup(IApplication app, IRuleContext context, IBizObj dInfo, ZipOutputStream zOut)
		{
			try
			{
				app.sessionData["InBackup"] = true;
				Debug.Assert(zOut != null && dInfo != null);
				string root = app.resMgr.resolve("$ConfigParam1$");
				string comment = dInfo.get("comments").ToString();
				string shortName = "";

				//Write the actual deployment info bizobj as a file, so that it can be recreated
				//just from the zip file.
				zOut.PutNextEntry(new ZipEntry(BackupManagementBizRules.DeploymentInfoFileName));
				TextWriter t = new StreamWriter(zOut);
				t.Write(dInfo.Xml);
				t.Flush();
				context.put("zipWriter", t);

				if (root.StartsWith("file://"))
					root = root.Substring(7);

				foreach (IBizObj company in dInfo.get("backupCompanies") as IBizObjList)
				{
					shortName = company.get("shortName").ToString();
					string templatePath = Cmn.pathCombine(app.resMgr.getMacro("BackupRestoreFileStore"), shortName);
					templatePath = Cmn.pathCombine(root, templatePath);

					zOut.SetComment(comment);

					if (BackupHelper.shouldPerform(dInfo, "includeSitePages"))
						processDirsAndFiles(zOut, Cmn.pathCombine(root, shortName), "", null, true, shortName);
				}
			}
			catch (Exception e)
			{
				throw new BackupRestoreDeployException("error executing createDeployment.", e);
			}
		}

		public void executeSiteDeploymentBackup(IApplication app, IRuleContext context, IBizObj dInfo, ZipOutputStream zOut)
		{
			Debug.Assert(zOut != null && dInfo != null);
			string root = app.resMgr.resolve("$ConfigParam1$");
			XmlDocument doc = new XmlDocument();
			string shortName = null;
			IBizObjList list = dInfo.get("features") as IBizObjList;
			foreach (IBizObj obj in list)
			{
				string xmlFilename = obj.get("xmlFile").ToString();
				string xml = Cmn.pathCombine(root, string.Format("configFiles{0}Features{0}{1}", Path.DirectorySeparatorChar, xmlFilename));
				if (File.Exists(xml))
				{
					doc.Load(xml);
					//processing bin
					XmlNodeList binDependencies = doc.DocumentElement.SelectNodes("Dependencies/Dependency[@type='binary']");
					foreach (XmlElement dep in binDependencies)
					{
						string fileName = Cmn.pathCombine(root.Substring(0, root.Length - 3), dep.InnerText);
						if (File.Exists(fileName))
						{
							string entryName = string.Format("bin/{0}", dep.InnerText);
							zipOutHelper(zOut, fileName, entryName, shortName);
						}
					}
					//processing dar 
					XmlNodeList darDependencies = doc.DocumentElement.SelectNodes("Catalogs/Entry");
					foreach (XmlElement dep in darDependencies)
					{
						string entryName = string.Format(@"configFiles/Catalogs/DataRequestDefinitions/{0}/", dep.InnerText);
						string dirName = Cmn.pathCombine(root, entryName);
						if (Directory.Exists(dirName))
						{
							string[] files = Directory.GetFiles(dirName, "*.xml");
							for (int j = 0; j < files.Length; j++)
								zipOutHelper(zOut, files[j]
									, files[j].Substring(files[j].LastIndexOf(Path.DirectorySeparatorChar) + 1), shortName);
						}
					}
					//process features backup
					string featurePath = string.Format("configFiles{0}Features{0}{1}", Path.DirectorySeparatorChar, xmlFilename);
					string file = Cmn.pathCombine(root, featurePath);
					zipOutHelper(zOut, file, featurePath, shortName);
				}
			}

			//process StartupConfigs
			string confPath = Cmn.pathCombine(root, "configFiles");
			if (true.Equals(dInfo.get("includeStartUpConfig")) && Directory.Exists(confPath))
			{
				string path = "configFiles" + Path.DirectorySeparatorChar;
				string[] files = Directory.GetFiles(Cmn.pathCombine(root, path));
				for (int i = 0; i < files.Length; i++)
					zipOutHelper(zOut, files[i]
						, path + files[i].Substring(files[i].LastIndexOf(Path.DirectorySeparatorChar) + 1)
						, shortName);

			}
			//process imagecache backup
			string cachePath = string.Format("configFiles{0}Catalogs{0}ImageCache{0}", Path.DirectorySeparatorChar);
			string cacheName = Cmn.pathCombine(root, string.Format("{0}{1}images.xml", cachePath, shortName));
			zipOutHelper(zOut, cacheName
				, cachePath + cacheName.Substring(cacheName.LastIndexOf(Path.DirectorySeparatorChar) + 1)
				, shortName);

			//process basefeatures backup
			if (true.Equals(dInfo.get("includeBaseFeature")))
			{
				string basePath = string.Format("configFiles{0}BaseFeatures{0}", Path.DirectorySeparatorChar);
				processDirsAndFiles(zOut, root, basePath, new string[] { "*.xml" }, false, shortName);
			}

			//process app dependency dirs
			string basePagePath = "";
			if (true.Equals(dInfo.get("overrideRootPage")))
			{
				///process base pages *.cs,*.config,*.dat, and 
				string[] filters = new string[] { "*.cs", "*.config", "*.dat", "*.asax" };
				processDirsAndFiles(zOut, root, "", filters, false, shortName);
				zipOutHelper(zOut, Cmn.pathCombine(root, "NitobiLiveStreamHandler.aspx"), "", shortName);
				zipOutHelper(zOut, Cmn.pathCombine(root, "AjaxRequestHandler.aspx"), "", shortName);
			}
			else
			{
				///process base pages *.aspx, *.cs,*.config,*.dat and ajax support aspx
				string[] filters = new string[] { "*.aspx", "*.cs", "*.config", "*.dat", "*.asax" };
				processDirsAndFiles(zOut, root, "", filters, false, shortName);
				basePagePath = shortName + Path.DirectorySeparatorChar;
			}

			//process root dependency directories.
			string dirlist = dInfo.get("directories").ToString();
			if (dirlist.Length > 0)
			{
				if (dirlist.Contains(","))
				{
					string[] appDirs = dInfo.get("directories").ToString().Split(',');
					for (int i = 0; i < appDirs.Length; i++)
						processDirsAndFiles(zOut, Cmn.pathCombine(root, appDirs[i]), appDirs[i] + Path.DirectorySeparatorChar, null, true, shortName);
				}
				else
					processDirsAndFiles(zOut, Cmn.pathCombine(root, dirlist), dirlist + Path.DirectorySeparatorChar, null, true, shortName);
			}
			//process catalogs
			if (true.Equals(dInfo.get("includeCatalogs")))
			{
				if (dInfo.get("catalogs").ToString().Contains(","))
				{
					string[] dirs = dInfo.get("catalogs").ToString().Split(',');
					for (int i = 0; i < dirs.Length; i++)
					{
						string featurePath = string.Format("configFiles{0}Catalogs{0}{1}{0}", Path.DirectorySeparatorChar, dirs[i]);
						processDirsAndFiles(zOut, Cmn.pathCombine(root, featurePath), featurePath, null, true, shortName);
					}
				}
				else if ("full".Equals(dInfo.get("catalogs").ToString().ToLower()))
				{
					string featurePath = string.Format("configFiles{0}Catalogs{0}", Path.DirectorySeparatorChar);
					processDirsAndFiles(zOut, Cmn.pathCombine(root, featurePath), featurePath, null, true, shortName);
				}
			}

			if (true.Equals(dInfo.get("includeBin")))
			{
				string[] files = Directory.GetFiles(root.Substring(0, root.Length - 3));
				for (int i = 0; i < files.Length; i++)
				{
					string entryName = string.Format("bin{0}{1}", Path.DirectorySeparatorChar
					, files[i].Substring(files[i].LastIndexOf(Path.DirectorySeparatorChar) + 1));
					zipOutHelper(zOut, files[i], entryName, shortName);
				}
			}
		}

		public string createTemplateDataBackup(IApplication app, IRuleContext context,INameAddressSupport info)
		{
			try
			{
				if (info.get("company") == null)
					throw new BackupRestoreDeployException("required company template is missing. error executing createTemplateDataBackup bizrule.");
				string root = app.resMgr.resolve("$ConfigParam1$");
				if (root.StartsWith("file://"))
					root = root.Substring(7);
				root = Cmn.pathCombine(root, string.Format("bktemp_{0}",info.get("company.shortName")));
				if (Directory.Exists(root))
				{
					string[] files = Directory.GetFiles(root);
					for (int i = 0; i < files.Length; i++)
						File.Delete(files[i]);
				}
				info.put("companyId", info.get("company.id") as Swaf.BGuid.Guid);
				info.put("objectTreeName", "Articles");
				info.put("includeSelf", true);
				info.put("listDetailLevel", DetailLevel.fullDetails);
				info.put("includeTranslations", true);
				info.put("defaultCacheName", info.get("company.settings.defaultMediaCache"));
				IResults rc = app.ruleMgr.execute("getArticles", info, (IBizObj)null);
				IBizObjList articles= null;
				if (rc.ExceptionInfo == null && rc.CallResults is IBizObjList)
				{
					articles= app.bizObjMgr.createList("Article");
					foreach (IBizObj book in (IBizObjList)rc.CallResults)
						articles.Add(book);
				}
				//process each book as xml
				if (articles != null)
				{
					foreach(IBizObj book in articles)
					{
						info.put("parentId", book.get("id"));
						info.put("entireTree", true);
						rc = app.ruleMgr.execute("getArticles", info, (IBizObj)null);
						if (rc.ExceptionInfo == null && rc.CallResults is IBizObjList)
						{
							IBizObjList templist = app.bizObjMgr.createList("Article");
							foreach (IBizObj child in (IBizObjList)rc.CallResults)
								templist.Add(child);
							Directory.CreateDirectory(root);
							//TODO:write for now to file system.
							//the info should pass in the zip file path for append
							string fileName = string.Format("{0}{1}{2}_dat.xml", root, Path.DirectorySeparatorChar, book.get("id"));
							File.WriteAllText(fileName,templist.Xml);
						}
					}
				}

				return root;
			}
			catch (Exception e)
			{
				throw new BackupRestoreDeployException("error executing createTemplateDataBackup.", e);
			}
		}
		
		public void addZipOutEntry(IApplication app, IRuleContext context, ZipOutputStream zOut, FileStream fileStream, string entryName)
		{

		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="zOut"></param>
		/// <param name="dirPath"></param>
		/// <param name="filters">all files will be zipout on the dirpath if this value is null</param>
		protected void processDirsAndFiles(ZipOutputStream zOut,string root, string dirPath,string[] filters,bool deep,string shortName)
		{
			try
			{
				string[] dirs = new string[] { };
				if (deep)
					dirs = Directory.GetDirectories(root);
				if (filters == null)
				{
					string[] files = Directory.GetFiles(root);
					for (int i = 0; i < files.Length; i++)
					{
						if (!files[i].EndsWith(".scc", StringComparison.OrdinalIgnoreCase))
						{
							string entry = string.Format("{0}{1}", dirPath, files[i].Substring(files[i].LastIndexOf(Path.DirectorySeparatorChar) + 1));
							zipOutHelper(zOut, files[i], entry, shortName);
						}
					}
				}
				else
					for (int j = 0; j < filters.Length; j++)
					{
						string[] files = Directory.GetFiles(Cmn.pathCombine(root, dirPath), filters[j]);
						for (int i = 0; i < files.Length; i++)
						{
							string entry = string.Format("{0}{1}", dirPath, files[i].Substring(files[i].LastIndexOf(Path.DirectorySeparatorChar) + 1));
							zipOutHelper(zOut, files[i], entry,shortName);
						}
					}

				for (int j = 0; j < dirs.Length; j++)
					if (dirs[j].Contains("."))
						processDirsAndFiles(zOut, dirs[j], dirs[j].Substring(dirs[j].LastIndexOf('.') + 1) + Path.DirectorySeparatorChar
							, filters, deep,shortName);
					else
						processDirsAndFiles(zOut, dirs[j], dirPath + dirs[j].Substring(dirs[j].LastIndexOf(Path.DirectorySeparatorChar) + 1) + Path.DirectorySeparatorChar
							, filters, deep,shortName);
			}
			catch (Exception e)
			{
				throw new BackupRestoreDeployException("error executing processDirsAndFiles.", e);
			}
		}


		protected void zipOutHelper(ZipOutputStream zOut, string filePath,string entryName,string shortName) 
		{
			try
			{
				if (File.Exists(filePath))
				{
					string location = shortName==null?string.Format("bu/{0}", entryName):string.Format("bu/{0}/{1}",shortName,entryName);
					zOut.PutNextEntry(new ZipEntry(location));
					FileStream binDll = File.OpenRead(filePath);
					int b;
					while ((b = binDll.ReadByte()) != -1)
						zOut.WriteByte((byte)b);
					binDll.Close();
					binDll.Dispose();
				}
			}
			catch (Exception e)
			{
				throw new BackupRestoreDeployException("error executing zipOutHelper.", e);
			}
		}
		
	}
	
}
